﻿using System;
using System.Reflection;
using nRoute.Components.Composition;
using nRoute.Utilities;

namespace nRoute.Mapping
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = false)]
    public class MapResourceAttribute
		 : MapResourceBaseAttribute
	{
		private const string RESOURCE_MUSTBE_OFTYPE = "{0} type must be an implementation of {1}.";

        private readonly string _name;
        private readonly Type _lifetimeManagerType;
        private Type _resourceType;
        private Type _implementationType;

        public MapResourceAttribute()
            : this(null, null, null) { }

        public MapResourceAttribute(string name)
            : this(null, name, null) { }

        public MapResourceAttribute(Type lifetimeManagerType)
            : this(null, null, lifetimeManagerType) { }

        public MapResourceAttribute(string name, Type lifetimeManagerType)
            : this(null, name, lifetimeManagerType) { }

        public MapResourceAttribute(Type resourceType, string name)
            : this(resourceType, name, null) { }

        public MapResourceAttribute(Type resourceType, Type lifetimeManagerType)
            : this(resourceType, null, lifetimeManagerType) { }

        public MapResourceAttribute(Type resourceType, string name, Type lifetimeManagerType)
        {
            _resourceType = resourceType;
            _name = name;
            _lifetimeManagerType = lifetimeManagerType;
        }

#region Properties

        public string Name
        {
            get { return _name; }
        }

        public Type LifetimeManagerType
        {
            get { return _lifetimeManagerType; }
        }

#endregion

#region Override

        protected internal override Type GetResourceType(Type targetType)
        {
            if (_resourceType == null) _resourceType = targetType;      // if no resource type is specified then the target type is given
            _implementationType = targetType;                           // implementation type is type the attribute was applied to
            return _resourceType;
        }

        protected internal override IResourceLocator GetResourceLocator(Type targetType, ITypeBuilder typeBuilder)
        {
			if (_resourceType != null) EnsureIsResourceType(targetType, _resourceType);
            var _lifetimeManager = (_lifetimeManagerType != null) ? (ILifetimeManager)typeBuilder.BuildType(_lifetimeManagerType) : new PerInstanceLifetimeManager();

            return new ResourceLocator(_resourceType, _implementationType, _lifetimeManager);
        }

#endregion

#region Helper
        
        protected static void EnsureIsResourceType(Type implementationType, Type serviceType)
        {
            if (!serviceType.IsAssignableFrom(implementationType)) throw new InvalidOperationException(
               string.Format(RESOURCE_MUSTBE_OFTYPE, implementationType.FullName, serviceType.FullName));
        }

#endregion

    }
}
